package com.weizhu.swufer.core.server;

import com.weizhu.swufer.core.annotation.BizRetry;
import com.weizhu.swufer.core.entity.InvokeParam;
import com.weizhu.swufer.core.entity.RpcBaseInfo;
import com.weizhu.swufer.core.exception.RpcBizException;
import com.weizhu.swufer.core.filter.Filter;
import com.weizhu.swufer.core.filter.FilterChain;
import com.weizhu.swufer.core.exception.RpcException;
import com.weizhu.swufer.core.rpc.RpcRequest;
import com.weizhu.swufer.core.util.ClassUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;

public class ServiceHandler implements Filter {

    private final static Logger logger = LoggerFactory.getLogger(ServiceHandler.class);

    private ConcurrentHashMap<String, RpcBaseInfo> localServiceMap;

    public ServiceHandler(ConcurrentHashMap<String, RpcBaseInfo> localServiceMap) {
        this.localServiceMap = localServiceMap;
    }

    @Override
    public Object doFilter(InvokeParam invokeParam, FilterChain filterChain) throws Exception{
        RpcRequest rpcRequest = invokeParam.getRpcRequest();
        String interfaceName = rpcRequest.getInterfaceName();
        Object bean = getBean(interfaceName);
        if (bean == null){
            throw new RpcException("Service bean '" + interfaceName + "' not found.");
        }
        Method method = null;
        String methodName = rpcRequest.getMethodName();
        Object[] args = rpcRequest.getArgs();

        Class<?>[] argTypes = rpcRequest.getArgTypes();
        if (argTypes == null){
            argTypes = ClassUtil.getParamsType(args);
        }

        method = bean.getClass().getMethod(methodName,argTypes);
        Object result;
        try {
            result = method.invoke(bean, args);
        }catch (InvocationTargetException e){
            Throwable trowEx = e.getTargetException();
            if (method.isAnnotationPresent(BizRetry.class)){
                throw new RpcBizException(1,trowEx.getMessage(),trowEx);
            }
            logger.error("",e);
            throw new RpcBizException(trowEx.getMessage(),trowEx);
        }catch (Exception e){
            if (method.isAnnotationPresent(BizRetry.class)){
                throw new RpcBizException(1,e.getMessage(),e);
            }
            logger.error("",e);
            throw new RpcBizException(e.getMessage(),e);
        }

        return result;
    }

    private RpcBaseInfo getRpcBaseInfo(String name){
        return localServiceMap.get(name);
    }

    private Object getBean(String name){
        RpcBaseInfo rpcBaseInfo = localServiceMap.get(name);
        if (rpcBaseInfo != null){
            return rpcBaseInfo.getTarget();
        }
        return null;
    }
}
